#pragma once


#include <jsoncpp/json/json.h>
#include <algorithm>
#include <queue>
#include "Index.hpp"
#include "Remove_Tag.hpp"
#include "Retrieve_Html.hpp"
#include "log.hpp"

class Searcher
{
    std::vector<std::shared_ptr<inverted_base>> Filter_files(const std::unordered_map<int , std::shared_ptr<inverted_base>>& all_file)
    {
        // 1. 设置小堆
        auto comp = [](const std::shared_ptr<inverted_base>& x , const std::shared_ptr<inverted_base> y)
        {
            return x->_weight > y->_weight;
        };
        std::priority_queue<std::shared_ptr<inverted_base> , std::vector<std::shared_ptr<inverted_base>> , decltype(comp)> pq(comp);
        int max_size = std::max(10.0 , (double)all_file.size()*0.4);

        // 2. 遍历所有文件,判断文件是否需要加入到堆中: 堆中数量<max_size || 当前文件的权重比堆顶的要大
        for(auto& [fileno , inv_ptr] : all_file)
        {
            if(pq.size() < max_size)
            {
                pq.push(inv_ptr);
            }
            else if(pq.top()->_weight < inv_ptr->_weight)
            {
                pq.pop();
                pq.push(inv_ptr);
            }
        }
        // 3. 将结果放回到数组中
        std::vector<std::shared_ptr<inverted_base>> ret;
        while(pq.size())
        {
            auto inv_ptr = pq.top();
            pq.pop();
            ret.push_back(inv_ptr);
        }
        reverse(ret.begin() , ret.end());
        return std::move(ret);
    }
public:
    Searcher()
    {
    }

    void BulidIndex(const std::string &raw_file)
    {
        // 1. 获取所有的url网址
        StripOutUrl sol(raw_file);
        sol.Start();
        std::vector<std::string> &all_url = sol.Get_url();
        int n = all_url.size(), num = 0;

        Get_HTML_files ghf;
        ghf.Init();

        for (auto &url : all_url)
        {
            ProgressBar(n , num++);   // 显示进度条
            // 2. 获取HTML文件
            std::shared_ptr<html_base> html_ptr = ghf.Get_Html(url);  
            // 3. 去标签
            RemoveTag()(html_ptr);
            // 4. 将HTML文件加入到索引中                                     
            _index.Insert(html_ptr);
        }
        std::cout << "\n";
    }

    std::string find(const std::string &query)
    {
        
        for(auto& ch : query)
            tolower(ch);
        // 1. 对query进行分词
        std::vector<std::string> query_words;
        std::shared_ptr<JiebaWrapper> Jbwrapper = JiebaWrapper::Get_Jieba();
        Jbwrapper->segment_noStop(query , query_words);
    
        // 2. 获取所有相关文件
        std::unordered_map<int , std::shared_ptr<inverted_base>> all_file;
        for(auto& word : query_words)
        {
            std::vector<std::shared_ptr<inverted_base>>& files = _index.Get_By_Word(word);
            // 将所有相关的文件加入到哈希表中
            for(auto inv_ptr : files)
            {
                int fileno = inv_ptr->_fileno;
                if(!all_file.count(inv_ptr->_fileno))
                {
                    all_file[fileno] = inv_ptr;
                }
                else
                {
                    all_file[fileno]->_weight += inv_ptr->_weight;
                }
            }
        }


        // 3. 筛选相关文件
        std::vector<std::shared_ptr<inverted_base>> relevant_files = Filter_files(all_file);

        // 4. 使用Json进行序列化
        Json::Value root;
        for(auto ivt_ptr: relevant_files)
        {
            Json::Value value;
            value["content"] = Get_content(ivt_ptr);
            value["url"] = Get_url(ivt_ptr);
            value["title"] = Get_title(ivt_ptr);

            root.append(value);
        }

        Json::StreamWriterBuilder builder;
        std::shared_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
        std::stringstream ss;
        writer->write(root , &ss);

        return ss.str();
    }


private:
    Index _index;

private:
    std::string Get_content(std::shared_ptr<inverted_base> ptr)
    {
        // 1. 获取对应文件
        std::shared_ptr<html_base> html_ptr = _index.Get_By_Fileno(ptr->_fileno);
        std::string& content = html_ptr->_content;
        int pos = content.find(ptr->_word);
        if(pos == std::string::npos) return "";

        // 2. 确定区间边界
        static int prev = 300, back = 500;
        int start = std::max(0 , pos - prev);
        int end = std::min((int)content.size() , pos + back); 

        // 3. 获取字符串进行返回
        std::string summary = content.substr(start , end - start);
        return std::move(summary);
    }
    std::string Get_url(std::shared_ptr<inverted_base> ptr)
    {
        std::string ret = _index.Get_By_Fileno(ptr->_fileno)->_url;
        return std::move(ret);
    }
    std::string Get_title(std::shared_ptr<inverted_base> ptr)
    {
        std::string ret = _index.Get_By_Fileno(ptr->_fileno)->_title;
        return std::move(ret);
    }
};
